/*
 * Copyright (C) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "napi/native_api.h"

#include <stdlib.h>
#include <string.h>

#include <iostream>
#include <vector>
#include <string>

#include "tar.h"

#include "hilog/log.h"

#define LOG_DOMAIN 0x0201
#define LOG_TAG "LZL"

#define GET_AND_THROW_LAST_ERROR(env)                                                                   \
    do {                                                                                                \
        const napi_extended_error_info* errorInfo = nullptr;                                            \
        napi_get_last_error_info((env), &errorInfo);                                                    \
        bool isPending = false;                                                                         \
        napi_is_exception_pending((env), &isPending);                                                   \
        if(!isPending && errorInfo != nullptr) {                                                       \
            const char* errorMessage =                                                                  \
                errorInfo->error_message != nullptr ? errorInfo->error_message : "empty error message"; \
            napi_throw_error((env), nullptr, errorMessage);                                             \
        }                                                                                               \
    } while(0)

#define NAPI_ASSERT_BASE(env, assertion, message, retVal)                                    \
    do {                                                                                     \
        if(!(assertion)) {                                                                  \
            napi_throw_error((env), nullptr, "assertion (" #assertion ") failed: " message); \
            return retVal;                                                                   \
        }                                                                                    \
    } while(0)

#define NAPI_ASSERT(env, assertion, message) NAPI_ASSERT_BASE(env, assertion, message, nullptr)

#define NAPI_CALL_BASE(env, theCall, retVal) \
    do {                                     \
        if((theCall) != napi_ok) {          \
            GET_AND_THROW_LAST_ERROR((env)); \
            return retVal;                   \
        }                                    \
    } while(0)

#define NAPI_CALL(env, theCall) NAPI_CALL_BASE(env, theCall, nullptr)

#define DECLARE_NAPI_FUNCTION(name, func)                                         \
    {                                                                             \
(name), nullptr,(func), nullptr, nullptr, nullptr, napi_default, nullptr \
    }

#define MAX_STRING_LENGTH 128
#define MAX_COMMEND_LENGTH 256


class Jtar {
private:
    std::string rootPath;   // 不能清除
    std::vector < std::string > TarPath;
    char** cTarPath = nullptr;
    bool mark = true;
public:

    Jtar() { }

    ~Jtar() {
        freeTar();
    }

    static Jtar& GetInstance() {
        static Jtar * instance;  // 单例
        if (instance == nullptr) {
            instance = new Jtar();
        }
        return *instance;
    }

    static Jtar& FreeInstance() {
        Jtar * instance = &GetInstance();  // 拿出单例
        if (instance == nullptr) {
            delete instance;    // 释放单例
        }
        return *instance;
    }

    void setTarRootPath(std::string Path) {
        rootPath = Path;
    }

    std::string getTarRootPath() {
        return rootPath;
    }

    void addTarpath(std::string path) {
        // 上层不能保证不会重复，需要自己重新去检验
        std::vector < std::string >::iterator it;   //声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素
        for (it = TarPath.begin(); it != TarPath.end(); it++)
        {
            if (path != *it) {
                // 如果存在删除原来的
                it = TarPath.erase(it);
            }
        }
        // 重新添加
        TarPath.push_back(path);
        mark = true;    // 发生修改进行标记
    }

    void delTarpath(std::string path) {
        std::vector < std::string >::iterator it;   //声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素
        for (it = TarPath.begin(); it != TarPath.end(); it++)
        {
            if (path != *it) {
                it = TarPath.erase(it);
                mark = true;    // 发生修改进行标记
            }
        }
    }

    // 释放 char** 的内存
    void FreeCString(char** destination, int size) {

        if (destination == nullptr) {
            return;
        }

        for (int n = 0; n < size; n++)
        {
            delete destination[n];
            destination[n] = nullptr;
        }
        //        delete[] destination;
        destination = nullptr;
    }

    // 将 vector中的所有string 转为一个 char**
    void MakeCString(const std::vector < std::string >& source, char**& destination)
    {
        // 释放资源
        FreeCString(destination, static_cast < int > (source.size()));
        // 注意释放内存
        destination = new char*[static_cast < int > (source.size())];
        for (int n = 0; n < static_cast < int > (source.size()); ++n)
        {
            destination[n] = new char[MAX_STRING_LENGTH];
            strcpy(destination[n], (rootPath + "/" + source[n]).c_str());
        }

    }

    char ** getTarPaths() {
        if (mark == true) { // 判断是否发生修改
            MakeCString(TarPath, cTarPath);
        }
        mark = false;   // 重置修改状态
        return cTarPath;
    }

    int getTarPathCount() {
        return TarPath.size();
    }

    std::string getcommendPath() {
        std::string commend = "";
        std::vector < std::string >::iterator it;   //声明一个迭代器，来访问vector容器，作用：遍历或者指向vector容器的元素
        for (it = TarPath.begin(); it != TarPath.end(); it++)
        {
            commend += "./" + (*it) + " ";
        }

        return commend;
    }

    void freeTar() {
        if (cTarPath != nullptr) {
            FreeCString(cTarPath, static_cast < int > (TarPath.size()));
        }
        TarPath.clear();
        mark = true;
    }

};

// 测试调用参数
static napi_value Test(napi_env env, napi_callback_info info) {
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    NAPI_ASSERT(env, valuetype0 == napi_string && valuetype1 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status status = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    char value1[MAX_STRING_LENGTH];
    size_t size1 = 0;
    status = napi_get_value_string_utf8(env, args[1], value1, MAX_STRING_LENGTH, &size1);

    napi_value sum;
    NAPI_CALL(env, napi_create_string_utf8(env, Jtar::GetInstance().getTarRootPath().c_str(), 128, &sum));
    return sum;
}

// 外部接口
static napi_value SetTarRootPath(napi_env env, napi_callback_info info) {
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    // 把路径保存进去
    Jtar::GetInstance().setTarRootPath(value0);
    //    Jtar::GetInstance().setTarRootPath(root);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];

    strcpy(retStr, "AddTarPath finish!");

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    return ret;
}

static napi_value AddTarPath(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "AddTarPath start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    // 把路径保存进去
    Jtar::GetInstance().addTarpath(value0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];

    strcpy(retStr, "AddTarPath finish!");

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));

    OH_LOG_INFO(LOG_APP, "AddTarPath stop");
    return ret;
}

static napi_value SiTar(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "SiTar start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];

    int res = Tar(value0, Jtar::GetInstance().getTarPaths(), Jtar::GetInstance().getTarPathCount());
    if (res < 0) {
        strcpy(retStr, "Tar create failure!");
    } else {
        strcpy(retStr, "Tar create success!");
    }

    // 清除tar工具的实例记录
    Jtar::GetInstance().freeTar();

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "SiTar stop");
    return ret;
}

static napi_value SiUnTar(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "SiUnTar start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];

    int res = UnTar(value0, nullptr, 0);
    if (res < 0) {
        strcpy(retStr, "UnTar create failure!");
    } else {
        strcpy(retStr, "UnTar create success!");
    }

    // 清除tar工具的实例
    Jtar::GetInstance().freeTar();

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "SiUnTar stop");
    return ret;
}

static napi_value SysTar(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "SysTar start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    char commend[MAX_COMMEND_LENGTH];
    sprintf(commend, "cd %s && tar -cf ./%s %s", Jtar::GetInstance().getTarRootPath().c_str(), value0, Jtar::GetInstance().getcommendPath().c_str());

    // 代码业务逻辑
    napi_value ret;
    char retStr[MAX_STRING_LENGTH];

    pid_t status = system(commend);
    if (status != -1 && WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
        strcpy(retStr, "dir create success!");
    } else {
        strcpy(retStr, "dir create failure!");
    }

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "SysTar stop");
    return ret;
}

static napi_value SysUnTar(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "SysUnTar start");
    // 获取参数
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    NAPI_ASSERT(env, valuetype0 == napi_string && valuetype1 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    char value1[MAX_STRING_LENGTH];
    size_t size1 = 0;
    stat = napi_get_value_string_utf8(env, args[1], value1, MAX_STRING_LENGTH, &size1);

    char commend[MAX_COMMEND_LENGTH];
    if (!strcmp(value1, "") || strlen(value1) <= 0) {
        sprintf(commend, "cd %s && tar -xvf ./%s -C ./", Jtar::GetInstance().getTarRootPath().c_str(), value0);
    } else {
        char tmpcommend[MAX_COMMEND_LENGTH];
        sprintf(tmpcommend, "mkdir -p %s/%s/", Jtar::GetInstance().getTarRootPath().c_str(), value1);
        system(tmpcommend);
        sprintf(commend, "cd %s && tar -xf ./%s -C ./%s/", Jtar::GetInstance().getTarRootPath().c_str(), value0, value1);
    }

    // 代码业务逻辑
    napi_value ret;
    char retStr[MAX_COMMEND_LENGTH];
    pid_t status = system(commend);    // s1.c_str()
    if (status != -1 && WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
        strcpy(retStr, "System untar success!");
    } else {
        strcpy(retStr, "System untar failure!");
    }

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "SysUnTar start");
    return ret;
}

static napi_value Jmkdir(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "Jmkdir start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);


    // 调用系统命令去创建
    char commend[MAX_COMMEND_LENGTH];
    sprintf(commend, "mkdir -p %s", value0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];
    pid_t status = system(commend);
    if (status != -1 && WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
        strcpy(retStr, "dir create success!");
    } else {
        strcpy(retStr, "dir create failure!");
    }

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "Jmkdir start");
    return ret;
}

static napi_value Jtouch(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "Jtouch start");
    // 获取参数
    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = { nullptr, nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    napi_valuetype valuetype1;
    NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));

    NAPI_ASSERT(env, valuetype0 == napi_string && valuetype1 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    char value1[MAX_STRING_LENGTH];
    size_t size1 = 0;
    stat = napi_get_value_string_utf8(env, args[1], value1, MAX_STRING_LENGTH, &size1);

    // 调用系统命令去创建
    char commend[MAX_COMMEND_LENGTH];
    sprintf(commend, "echo \"%s\" >> %s", value1, value0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];
    pid_t status = system(commend);
    if (status != -1 && WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
        strcpy(retStr, "dir create success!");
    } else {
        strcpy(retStr, "dir create failure!");
    }

    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "Jtouch stop");
    return ret;
}

static napi_value Jrmdir(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "Jrmdir start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    // 调用系统命令去创建
    char commend[MAX_COMMEND_LENGTH];
    sprintf(commend, "rm -rf %s", value0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];
    pid_t status = system(commend);
    if (status != -1 && WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
        strcpy(retStr, "dir create success!");
    } else {
        strcpy(retStr, "dir create failure!");
    }
    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "Jrmdir stop");
    return ret;
}

static napi_value Jrmfile(napi_env env, napi_callback_info info) {
    OH_LOG_INFO(LOG_APP, "Jrmfile start");
    // 获取参数
    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = { nullptr };
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments");

    napi_valuetype valuetype0;
    NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));

    NAPI_ASSERT(env, valuetype0 == napi_string, "Wrong argument type. Numbers expected.");

    char value0[MAX_STRING_LENGTH];
    size_t size0 = 0;
    napi_status stat = napi_get_value_string_utf8(env, args[0], value0, MAX_STRING_LENGTH, &size0);

    // 调用系统命令去创建
    char commend[MAX_STRING_LENGTH];
    sprintf(commend, "rm %s", value0);

    napi_value ret;
    char retStr[MAX_STRING_LENGTH];
    pid_t status = system(commend);
    if (status != -1 && WIFEXITED(status) && 0 == WEXITSTATUS(status)) {
        strcpy(retStr, "dir create success!");
    } else {
        strcpy(retStr, "dir create failure!");
    }
    NAPI_CALL(env, napi_create_string_utf8(env, retStr, strlen(retStr), &ret));
    OH_LOG_INFO(LOG_APP, "Jrmfile stop");
    return ret;
}

EXTERN_C_START

/*
 * function for module exports
 */
static napi_value Init(napi_env env, napi_value exports)
{
    /*
     * Properties define
     */
    napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("setTarRootPath", SetTarRootPath),
    DECLARE_NAPI_FUNCTION("addTarPath", AddTarPath),
    DECLARE_NAPI_FUNCTION("sitar", SiTar),
    DECLARE_NAPI_FUNCTION("siuntar", SiUnTar),
    DECLARE_NAPI_FUNCTION("systar", SysTar),
    DECLARE_NAPI_FUNCTION("sysuntar", SysUnTar),
    DECLARE_NAPI_FUNCTION("jmkdir", Jmkdir),
    DECLARE_NAPI_FUNCTION("jtouch", Jtouch),
    DECLARE_NAPI_FUNCTION("jrmdir", Jrmdir),
    DECLARE_NAPI_FUNCTION("jrmfile", Jrmfile),
    DECLARE_NAPI_FUNCTION("test", Test), };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));

    return exports;
}
EXTERN_C_END

/*
 * Module define
 */
static napi_module
tarPlugin = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "libjtar",
.nm_priv = ((void *)0),
.reserved = {
0 },
};

/*
 * Module register function
 */
extern "C" __attribute__((constructor)) void RegisterModule(void){
napi_module_register(& tarPlugin);
}
